#include "console.h"
#include "ringbuf.h"
#include <string.h>
#include "port.h"
typedef struct console_dev
{
#ifdef CONSOLE_USE_ASNYC_SEND
    ringbuf_t tx_rbuf;
    uint8_t   tx_rbuf_space[CONSOLE_TX_RBUF_SIZE];
    uint8_t   tx_lbuf[CONSOLE_TX_LBUF_SIZE];
    uint8_t   tx_busy;
    int32_t   tx_cnt;
#endif
    
#ifdef CONSOLE_USE_ASNYC_RECV
    ringbuf_t rx_rbuf;
    uint8_t   rx_rbuf_space[CONSOLE_RX_RBUF_SIZE];
    uint8_t   rx_lbuf[CONSOLE_RX_LBUF_SIZE];
    uint8_t   rx_busy;
    uint16_t  rx_done_cnt;
    uint16_t  rx_done_cnt_pre;
    int32_t   rx_cnt;
#endif
    
    console_dev_ops_t ops;
}console_dev_t;

static console_dev_t g_console;

#ifdef CONSOLE_USE_ASNYC_SEND
static void console_async_send_start(void)
{
    int32_t ret;
    console_dev_t *console = &g_console;
    
    console->tx_cnt = ringbuf_data_size(&console->tx_rbuf);
    
    if(console->tx_cnt && !console->tx_busy)
    {
        console->tx_cnt = ringbuf_get(&console->tx_rbuf, console->tx_lbuf, CONSOLE_TX_LBUF_SIZE);
        console->tx_busy = 1;
        ret = console->ops.start_send(console->tx_lbuf, console->tx_cnt);
        //If send failed, put the data back to ring buffer.
        if(ret)
            ringbuf_put(&console->tx_rbuf, console->tx_lbuf, console->tx_cnt);
    }
}

static int32_t console_start_send(uint8_t *buf, int32_t size)
{
    console_dev_t *console = &g_console;
    
    LOOPSYS_DISABLE_IRQ;
    
    //Put data into ringbuf directly. If space is not enough, new data will be dropped.
    ringbuf_put(&console->tx_rbuf, buf, size);
    //Trigger send if it has not started
    console_async_send_start();
    
    LOOPSYS_ENABLE_IRQ;
    
    return size;
}

void console_send_done(void)
{
    console_dev_t *console = &g_console;
    
    console->tx_busy = 0;
    
    //Restart send
    console_async_send_start();
}
#endif

int32_t console_send(uint8_t *buf, int32_t size)
{
    int32_t len;
    
#ifdef CONSOLE_USE_ASNYC_SEND
    len = console_start_send(buf, size);
#else
    len = g_console.ops.poll_send(buf, size);
#endif

    return len;
}

#ifdef CONSOLE_USE_ASNYC_RECV
static void console_async_recv_start(void)
{
    console_dev_t *console = &g_console;
        
    if(!console->rx_busy)
    {
        console->rx_busy = 1;
        console->rx_cnt = CONSOLE_RX_LBUF_SIZE;
        console->ops.start_recv(console->rx_lbuf, console->rx_cnt);
    }
}

static int32_t console_start_recv(uint8_t *buf, int32_t size)
{
    int32_t len;
    console_dev_t *console = &g_console;
    
    LOOPSYS_DISABLE_IRQ;
    
    //Get data from ring buffer directly.
    len = ringbuf_get(&console->rx_rbuf, buf, size);
   
    //Trigger reveive
    console_async_recv_start();
    
    LOOPSYS_ENABLE_IRQ;
    
    return len;
}

void console_recv_done(void)
{
    console_dev_t *console = &g_console;
    
    console->rx_busy = 0;
    
    //Copy data from line buffer to ring buffer.
    //If space is not enough, new data will be dropped
    ringbuf_put(&console->rx_rbuf, console->rx_lbuf, console->rx_cnt);
    
    //Restart receive
    console_async_recv_start();
    
    console->rx_done_cnt++;
}
#endif

int32_t console_recv(uint8_t *buf, int32_t size)
{
    int32_t len;
    
#ifdef CONSOLE_USE_ASNYC_RECV
    len = console_start_recv(buf, size);
#else
    len = g_console.ops.poll_recv(buf, size);
#endif
    return len;
}

int32_t console_recv_buf_size(void)
{
    return ringbuf_data_size(&g_console.rx_rbuf);
}

void console_init(console_dev_ops_t *ops)
{
    console_dev_t *console = &g_console;
    
    memset(console, 0, sizeof(console_dev_t));

#ifdef CONSOLE_USE_ASNYC_SEND
    ringbuf_init(&console->tx_rbuf, console->tx_rbuf_space, CONSOLE_TX_RBUF_SIZE);
#endif
#ifdef CONSOLE_USE_ASNYC_RECV
    ringbuf_init(&console->rx_rbuf, console->rx_rbuf_space, CONSOLE_RX_RBUF_SIZE);
#endif
    
    if(ops)
        console->ops = *ops;
    
}

void console_check_recv_idle(void)
{
    console_dev_t *console = &g_console;
    
    if(console->rx_done_cnt == console->rx_done_cnt_pre)
    {
        console->rx_cnt = console->ops.recv_count();
        //If rx_cnt is not 0 which means there is data left in lbuf,
        //then stop rx and call console_recv_done(get data and restart rx).
        if(console->rx_cnt)
        {
            console->ops.stop_recv();
            console_recv_done();
        }
    }else{
        console->rx_done_cnt_pre = console->rx_done_cnt;
    }
}


#include "loopsys.h"

static void console_task_func(loop_task_t *self, uint32_t events)
{
    console_check_recv_idle();
    loopsys_event_clear_all(self);
}

void console_task_init(void)
{
    loop_task_t *task;
    
    task = loopsys_task_add(LOOPSYS_TASK_NUM - 2, console_task_func);
    if(task)
    {
        loopsys_task_timer_enable(task, LOOPSYS_TIMER_FOREVER, 50);
        loopsys_task_timer_start(task);
    }
}

